from app.functions import *
from app.config import SysConfig
from datetime import datetime
import json
from urllib.parse import quote, unquote
import hashlib
import hmac
import binascii
from urllib import request, error


class HuaWeiDNS:
    api_url = 'https://dns.cn-east-4.myhuaweicloud.com'
    API_VERSION = "2021-03-23"
    DateFormat = "%Y%m%dT%H%M%SZ"
    Algorithm = "SDK-HMAC-SHA256"

    def __init__(self, config):
        self.config = {
            'ak': config.get('ak', ''),
            'sk': config.get('sk', '')
        }

    def ListPublicZones(self, params={}):
        path = "/v2/zones"
        method = "GET"
        queryData = {
            "name": params.get('domain', ''),
            "limit": "500",
            "offset": "0",
        }
        buildData = self.buildQueryData(path, method, queryData)
        return self.sendHttpRequest(buildData.get('url'), method, [], buildData.get('header'))

    def CreateRecord(self, zoneId, recordName, recordType, value, params={}):
        path = '/v2/zones/' + str(zoneId) + '/recordsets'
        method = "POST"
        bodyData = {
            "name": recordName,
            "type": recordType,
            "records": [value],
        }
        if 'TTL' in params:
            bodyData['ttl'] = str(params.get('TTL', 600))

        buildData = self.buildQueryData(path, method, {}, bodyData)
        return self.sendHttpRequest(buildData.get('url'), method, bodyData, buildData.get('header'))

    def ModifyRecord(self, zoneId, recordId, recordName, recordType, value, params={}):
        path = '/v2/zones/' + str(zoneId) + '/recordsets/' + str(recordId)
        method = "PUT"
        bodyData = {
            "name": recordName,
            "type": recordType,
            "records": [value],
        }
        if 'TTL' in params:
            bodyData['ttl'] = str(params.get('TTL', 600))

        buildData = self.buildQueryData(path, method, {}, bodyData)
        return self.sendHttpRequest(buildData.get('url'), method, bodyData, buildData.get('header'))

    def DescribeRecordList(self, zoneId, params={}):
        path = '/v2/zones/' + str(zoneId) + '/recordsets'
        method = "GET"
        queryData = {
            "name": params.get('subDomain', ''),
            "type": params.get('recordType', ''),
            "limit": "500",
            "offset": "0",
        }
        buildData = self.buildQueryData(path, method, queryData)
        return self.sendHttpRequest(buildData.get('url'), method, [], buildData.get('header'))

    def DeleteRecord(self, zoneId, recordId):
        path = '/v2/zones/' + str(zoneId) + '/recordsets/' + str(recordId)
        method = "DELETE"
        buildData = self.buildQueryData(path, method, {})
        return self.sendHttpRequest(buildData.get('url'), method, {}, buildData.get('header'))

    def buildQueryData(self, path, method, queryData={}, bodyData={}):
        time = datetime.utcnow()
        queryString = ""
        header = {
            "content-type": "application/json",
            "X-Sdk-Date": datetime.strftime(time, self.DateFormat)
        }
        signedHeaders = self.SignedHeaders(header)
        CanonicalURI = self.CanonicalURI(path)
        CanonicalQueryString = self.CanonicalQueryString(queryData)
        canonicalHeaders = self.CanonicalHeaders(header, signedHeaders)
        hencode = self.findHeader(header, 'x-sdk-content-sha256')

        if hencode is None:
            bodyStr = ""
            if bodyData:
                bodyStr = json.dumps(bodyData, ensure_ascii=False)
            hencode = self.HexEncodeSHA256Hash(bodyStr.encode('utf-8'))
        canonicalRequest = "%s\n%s\n%s\n%s\n%s\n%s" % (method.upper(), CanonicalURI, CanonicalQueryString,
                                                       canonicalHeaders, ";".join(signedHeaders), hencode)

        stringToSign = self.StringToSign(canonicalRequest, time)
        signature = self.SignStringToSign(stringToSign, self.config.get('sk'))
        authValue = self.AuthHeaderValue(signature, self.config.get('ak'), signedHeaders)
        header["Authorization"] = authValue
        if CanonicalQueryString:
            queryString = "?" + CanonicalQueryString
        url = self.api_url + path + queryString
        return {
            "url": url,
            "header": header,
        }

    def StringToSign(self, request, time):
        b = self.HexEncodeSHA256Hash(request.encode('utf-8'))
        return "%s\n%s\n%s" % ("SDK-HMAC-SHA256", datetime.strftime(time, self.DateFormat), b)

    def SignStringToSign(self, strToSign, sigKey):
        hmac = self.hmacsha256(sigKey, strToSign)
        return binascii.hexlify(hmac).decode()

    @staticmethod
    def findHeader(headers, findKey):
        for header in headers:
            if header.lower() == findKey.lower():
                return headers[header]
        return None

    @staticmethod
    def urlencode(str):
        return quote(str, safe='~')

    @staticmethod
    def SignedHeaders(headers):
        arr = []
        for k in headers:
            arr.append(k.lower())
        arr.sort()
        return arr

    @staticmethod
    def CanonicalURI(uriStr):
        patterns = unquote(uriStr).split('/')
        uri = []
        for value in patterns:
            uri.append(HuaWeiDNS.urlencode(value))
        url_path = "/".join(uri)
        if url_path[-1] != '/':
            url_path = url_path + "/"
        return url_path

    @staticmethod
    def CanonicalQueryString(queryarams={}):
        keys = []
        for key in queryarams:
            keys.append(key)
        keys.sort()
        arr = []
        for key in keys:
            ke = HuaWeiDNS.urlencode(key)
            value = queryarams[key]
            kv = ke + "=" + HuaWeiDNS.urlencode(str(value))
            arr.append(kv)
        return '&'.join(arr)

    @staticmethod
    def CanonicalHeaders(headerData={}, signedHeaders={}):
        arr = []
        _headers = {}
        for k in headerData:
            keyEncoded = k.lower()
            value = headerData[k]
            valueEncoded = value.strip()
            _headers[keyEncoded] = valueEncoded
            if sys.version_info.major == 3:
                headerData[k] = valueEncoded.encode("utf-8").decode('iso-8859-1')
        for k in signedHeaders:
            arr.append(k + ":" + _headers[k])
        return '\n'.join(arr) + "\n"

    @staticmethod
    def HexEncodeSHA256Hash(d):
        sha = hashlib.sha256()
        sha.update(d)
        return sha.hexdigest()

    @staticmethod
    def hmacsha256(byte, msg):
        return hmac.new(byte.encode('utf-8'), msg.encode('utf-8'), digestmod=hashlib.sha256).digest()

    @staticmethod
    def AuthHeaderValue(sig, AppKey, sHeaders):
        return "%s Access=%s, SignedHeaders=%s, Signature=%s" % (
            "SDK-HMAC-SHA256", AppKey, ";".join(sHeaders), sig)

    @staticmethod
    def sendHttpRequest(url, method="GET", params={}, headerData={}):
        try:
            data = ""
            if params:
                data = json.dumps(params)
            req = request.Request(url, headers=headerData, data=data.encode('utf-8'), method=method)
            resp = request.urlopen(req)
            httpCode = resp.getcode()
            responseData = resp.read()
            responseContent = json.loads(responseData.decode('utf-8'))
            if httpCode != 200 and httpCode != 202:
                return funReturn(SysConfig.STATUS_CODE_INSIDEERROR, 'requst failed')
            return funReturn(SysConfig.STATUS_CODE_SUCCESS, 'success', responseContent)
        except error.HTTPError as e:
            # 当状态码不是 2xx时
            errorContent = e.read().decode("utf-8")
            errorContent = json.loads(errorContent)
            msg = errorContent.get('error_msg', '')
            code = errorContent.get('error_code', '')
            if not code:
                code = errorContent.get('code', '')
            if not msg:
                msg = errorContent.get('message', '')
            msg = f"{code}:{msg}" if code else msg
            return funReturn(SysConfig.STATUS_CODE_INSIDEERROR, msg)
        # except error.URLError as e:
        #     # 处理网络问题或证书错误等
        #     print("URL错误:", e.reason)
        except Exception as e:
            return funReturn(SysConfig.STATUS_CODE_INSIDEERROR, str(e))
